package cn.com.hhrcw.base.data.web;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotBlank;

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import cn.com.hhrcw.area.entity.SysArea;
import cn.com.hhrcw.area.service.ISysAreaService;
import cn.com.hhrcw.base.BaseParams;
import cn.com.hhrcw.base.data.mapstruct.BlockConvert;
import cn.com.hhrcw.base.data.mapstruct.BlockDataConvert;
import cn.com.hhrcw.base.data.mapstruct.SysAreaConvert;
import cn.com.hhrcw.base.data.vo.BlockDataVO;
import cn.com.hhrcw.base.data.vo.BlockVO;
import cn.com.hhrcw.base.data.vo.EnterpriseVO;
import cn.com.hhrcw.base.data.vo.SessionVO;
import cn.com.hhrcw.base.data.vo.SysAreaVO;
import cn.com.hhrcw.block.entity.Block;
import cn.com.hhrcw.block.entity.BlockData;
import cn.com.hhrcw.block.service.IBlockDataService;
import cn.com.hhrcw.block.service.IBlockService;
import cn.com.hhrcw.entity.CmsContent;
import cn.com.hhrcw.entity.EnInfo;
import cn.com.hhrcw.entity.OpJob;
import cn.com.hhrcw.entity.SsSession;
import cn.com.hhrcw.entity.SysUser;
import cn.com.hhrcw.es.entity.JobPositionDocument;
import cn.com.hhrcw.es.entity.RecruitmentPositionDocument;
import cn.com.hhrcw.es.entity.UniversityDocument;
import cn.com.hhrcw.es.entity.UniversitySpecialityDocument;
import cn.com.hhrcw.es.mapstruct.ElasticsearchConvert;
import cn.com.hhrcw.es.service.IElasticsearchService;
import cn.com.hhrcw.industry.mapstruct.RecruitmentPositionConvert;
import cn.com.hhrcw.industry.mapstruct.SysIndustryConvert;
import cn.com.hhrcw.industry.vo.RecruitmentPositionVO;
import cn.com.hhrcw.industry.vo.SysIndustryVO;
import cn.com.hhrcw.personal.cms.vo.CmsContentVO;
import cn.com.hhrcw.position.entity.RecruitmentPosition;
import cn.com.hhrcw.position.entity.SysIndustry;
import cn.com.hhrcw.position.service.IRecruitmentPositionService;
import cn.com.hhrcw.position.service.ISysIndustryService;
import cn.com.hhrcw.service.ICmsContentService;
import cn.com.hhrcw.service.ICvWorkIntentionService;
import cn.com.hhrcw.service.IEnInfoService;
import cn.com.hhrcw.service.IOpJobService;
import cn.com.hhrcw.service.ISsSessionService;
import cn.com.hhrcw.utils.SubjectUtils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 基础数据查询
 *
 * @author lixingxing
 * @date 2020年11月07日10:35:10
 */
@RestController
@RequestMapping("/base")
@Validated
@Slf4j
public class BaseDataController extends JeecgController<RecruitmentPosition, IRecruitmentPositionService> {

    @Autowired
    private ISysIndustryService sysIndustryService;

    @Autowired
    private IRecruitmentPositionService recruitmentPositionService;

    @Autowired
    private ISysAreaService sysAreaService;

    @Autowired
    private IBlockService blockService;

    @Autowired
    private IBlockDataService blockDataService;

    @Autowired
    private IOpJobService opJobService;

    @Autowired
    private IEnInfoService enInfoService;

    @Autowired
    private ICvWorkIntentionService cvWorkIntentionService;

    @Autowired
    private BlockDataConvert blockDataConvert;

    @Autowired
    private BlockConvert blockConvert;

    @Autowired
    private SysIndustryConvert sysIndustryConvert;

    @Autowired
    private RecruitmentPositionConvert recruitmentPositionConvert;

    @Autowired
    private ElasticsearchConvert elasticsearchConvert;

    @Autowired
    private SysAreaConvert sysAreaConvert;

    @Autowired
    private ICmsContentService cmsContentService;

    @Autowired
    private ISsSessionService sessionService;

    @Autowired
    private IElasticsearchService elasticsearchService;

    /**
     * 数据搜索
     * <pre>
     *      分词搜索: [字段].keywords=网裕测试
     *      精确匹配: [字段].term=民营
     *      大于等于: [字段].gte=1
     *      小于等于: [字段].lte=1
     *      模糊匹配: [字段].like=工程师*    工程师*: 前缀匹配,  *工程师: 后缀匹配
     *      范围搜索: [字段].range=2000,3000
     *      分词搜索多字段匹配: [字段1].keywords,[字段2].keywords=网裕测试
     * <pre/>
     * @param type     数据分类 position 职位, university 学校, speciality 专业, job 职位
     * @return
     */
    @GetMapping("/so")
    @AutoLog
    public Result<?> so(@NotBlank(message = "数据分类必填") String type, BaseParams params, HttpServletRequest request) {
        SysUser user = (SysUser) SubjectUtils.getSubject();
        Map<String, String[]> map = request.getParameterMap();
        boolean experienceRange = map.containsKey("experience.range");
        if (experienceRange) {
            String[] strings = map.get("experience.range");
            map.put("minExperience.gte", new String[]{strings[0].split(",")[0]});
            map.put("maxExperience.lte", new String[]{strings[0].split(",")[1]});
            map.remove("experience.range");
        }
        String[] strings = null;
        boolean moneyRange = map.containsKey("money.range");
        if (moneyRange) {
            strings = map.get("money.range");
            map.remove("money.range");
        }
        SearchRequest searchRequest = elasticsearchService.build(map);
        if (ArrayUtil.isNotEmpty(strings)) {
            strings = strings[0].split(",");
            BoolQueryBuilder builder = (BoolQueryBuilder) searchRequest.source().query();
            BoolQueryBuilder should = QueryBuilders.boolQuery();
            should.should(QueryBuilders.rangeQuery("minMoney").lte(strings[1]).gte(strings[0]));
            should.should(QueryBuilders.rangeQuery("maxMoney").lte(strings[1]).gte(strings[0]));
            builder.must(should);
        }

        Object val = null;
        switch (type) {
            case "position":
                val = elasticsearchService.search(searchRequest, RecruitmentPositionDocument.class);
                break;
            case "university":
                val = elasticsearchService.search(searchRequest, UniversityDocument.class);
                break;
            case "speciality":
                val = elasticsearchService.search(searchRequest, UniversitySpecialityDocument.class);
                break;
            case "job":
                //智能匹配
                List<SortBuilder<?>> sorts = searchRequest.source().sorts();
                if (sorts == null) {
                    searchRequest.source().sort(SortBuilders.scoreSort());
                } else {
                    sorts.add(0, SortBuilders.scoreSort());
                }
                searchRequest.source().sort("refreshNo", SortOrder.DESC).sort("publishTime", SortOrder.DESC);
                val = elasticsearchService.searchForPage(searchRequest, params.page(), JobPositionDocument.class);
                break;
            case "job_h5":
                map.put("companyId.term", new String[]{"1430782619972521986"});
                searchRequest = elasticsearchService.build(map);
                searchRequest.source().sort("publishTime", SortOrder.DESC);
                val = elasticsearchService.searchForPage(searchRequest, params.page(), JobPositionDocument.class);
                break;
            default:
                return Result.error("数据分类错误");
        }
        return Result.ok(val);
    }

    /**
     * 数据查询
     *
     * @param clz  数据分类 industry 行业, position 职位职类, area 地区数据, skills 技能
     * @param code 技能分类必填, 职位id
     * @return
     */
    @GetMapping("/data")
    public Result<Object> base(String code, @NotBlank(message = "数据分类必填") String clz) {
        Object val;
        switch (clz) {
            case "industry":
                val = this.industry();
                break;
            case "position":
                val = this.position();
                break;
            case "area":
                val = this.sysArea();
                break;
            case "provincesAndCitys":
                val = this.provincesAndCitys();
                break;
            case "skills":
                JSONArray array = (JSONArray) redisTemplate.opsForValue().get("skills_all");
                val = array.stream().filter(e -> {
                    JSONObject object = (JSONObject) e;
                    return StrUtil.equals(object.getString("positionId"), code);
                }).collect(Collectors.toList());
                break;
            default:
                return Result.error("数据分类错误");
        }
        return Result.ok(val);
    }
    /*
     * 省+市+区
     */
    public List<SysAreaVO> sysArea() {
        List<SysArea> sysAreas = sysAreaService.lambdaQuery().list();
        List<SysArea> provinces = sysAreas.stream().filter(e -> StrUtil.equals("province", e.getLevel())).collect(Collectors.toList());
        List<SysArea> citys = sysAreas.stream().filter(e -> StrUtil.equals("city", e.getLevel())).collect(Collectors.toList());
        List<SysArea> districts = sysAreas.stream().filter(e -> StrUtil.equals("district", e.getLevel())).collect(Collectors.toList());
        //省
        List<SysAreaVO> provinceVos = this.sysAreaConvert.toVO(provinces);
        for (SysAreaVO provinceVO : provinceVos) {
            //市
            List<SysAreaVO> cityVos = citys.stream().filter(e -> StrUtil.equals(e.getPid(), provinceVO.getId())).map(sysAreaConvert::toVO).collect(Collectors.toList());
            provinceVO.setChildren(cityVos);
            for (SysAreaVO cityVO : cityVos) {
                //区
                List<SysAreaVO> districtVos = districts.stream().filter(e -> StrUtil.equals(e.getPid(), cityVO.getId())).map(sysAreaConvert::toVO).collect(Collectors.toList());
                cityVO.setChildren(districtVos);
            }
        }
        return provinceVos;
    }
    /*
     * 省+市
     */
    public List<SysAreaVO> provincesAndCitys() {
        List<SysArea> sysAreas = sysAreaService.lambdaQuery().list();
        List<SysArea> provinces = sysAreas.stream().filter(e -> StrUtil.equals("province", e.getLevel())).collect(Collectors.toList());
        List<SysArea> citys = sysAreas.stream().filter(e -> StrUtil.equals("city", e.getLevel())).collect(Collectors.toList());
        //省
        List<SysAreaVO> provinceVos = this.sysAreaConvert.toVO(provinces);
        for (SysAreaVO provinceVO : provinceVos) {
            //市
            List<SysAreaVO> cityVos = citys.stream().filter(e -> StrUtil.equals(e.getPid(), provinceVO.getId())).map(sysAreaConvert::toVO).collect(Collectors.toList());
            provinceVO.setChildren(cityVos);
        }
        return provinceVos;
    }
    /**
     * 行业
     *
     * @return
     */
    public List<SysIndustryVO> industry() {
        List<SysIndustry> industryList = sysIndustryService.lambdaQuery().list();
        //一级行业
        List<SysIndustryVO> collect = industryList.stream().filter(e -> StrUtil.isBlank(e.getPCode())).map(sysIndustryConvert::toVO).collect(Collectors.toList());
        for (SysIndustryVO vo : collect) {
            //二级行业
            List<SysIndustryVO> children = industryList.stream().filter(e -> StrUtil.equals(e.getPCode(), vo.getCode())).map(sysIndustryConvert::toVO).collect(Collectors.toList());
            vo.setChildren(children);
        }
        return collect;
    }

    /**
     * 职位职类
     *
     * @return
     */
    public List<RecruitmentPositionVO> position() {
        List<RecruitmentPosition> positions = recruitmentPositionService.list();
        //一级职类
        List<RecruitmentPositionVO> collect = positions.stream().filter(e -> StrUtil.isBlank(e.getPCode())).map(recruitmentPositionConvert::toVO).collect(Collectors.toList());
        for (RecruitmentPositionVO vo : collect) {
            //二级职类
            List<RecruitmentPositionVO> children = positions.stream().filter(e -> StrUtil.equals(e.getPCode(), vo.getCode())).map(recruitmentPositionConvert::toVO).collect(Collectors.toList());
            vo.setChildren(children);
            for (RecruitmentPositionVO svo : children) {
                //三级职位
                List<RecruitmentPositionVO> last = positions.stream().filter(e -> StrUtil.equals(e.getPCode(), svo.getCode())).map(recruitmentPositionConvert::toVO).collect(Collectors.toList());
                svo.setChildren(last);
            }
        }
        return collect;
    }

    /**
     * 区块查询
     *
     * @param code
     * @return
     */
    @GetMapping("/block")
    public Result<BlockVO> block(@NotBlank(message = "code必填") String code) {
        Block block = this.blockService.lambdaQuery().eq(Block::getCode, code).one();
        Assert.notNull(block, "区块编码错误");
        return Result.success(this.child(blockConvert.toVO(block)));
    }

    private BlockVO child(BlockVO vo) {
        List<Block> data = blockService.lambdaQuery().eq(Block::getParentId, vo.getId()).orderByAsc(Block::getSequence).list();
        if (CollUtil.isEmpty(data)) {
            List<BlockData> blockData = this.blockDataService.lambdaQuery().eq(BlockData::getBlockId, vo.getId()).eq(BlockData::getDisabled, false).orderByAsc(BlockData::getSequence).orderByDesc(BlockData::getCreateTime).list();
            List<BlockDataVO> vos = blockDataConvert.toVO(blockData);
            //推荐职位
            boolean position = StrUtil.equals("position", vo.getType());
            boolean company = StrUtil.equals("company", vo.getType());
            boolean cmsContent = StrUtil.equals("cms_content", vo.getType());
            boolean special = StrUtil.equals("special", vo.getType());
            if (position) {
                vos = vos.stream().filter(e -> {
                    //仅允许不为null的条件，因为是逻辑删除，按道理不应该出现该情况，但2021年1月28日左右，正式环境、测试环境，均出现了该问题
                    OpJob job = this.opJobService.getById(e.getSourceId());
                    return job != null;
                }).map(e -> {
                    OpJob job = this.opJobService.getById(e.getSourceId());
                    EnInfo enInfo = this.enInfoService.getById(job.getCompanyId());
                    return blockDataConvert.toJobPosition(job, enInfo);
                }).collect(Collectors.toList());
            } else if (company) {
                //热门企业
                vos = vos.stream().map(e -> {
                    EnInfo enInfo = this.enInfoService.getById(e.getSourceId());
                    EnterpriseVO enInfoVO = blockDataConvert.toEnterprise(enInfo);
                    enInfoVO.setImage(enInfo.getEnLogo());
                    enInfoVO.setSourceId(enInfo.getId());
                    enInfoVO.setTitle(enInfo.getEnName());
                    enInfoVO.setSocialRecruitment(opJobService.getJobNatureCount(enInfo.getId(), "0-"));
                    enInfoVO.setCampusRecruitment(opJobService.getJobNatureCount(enInfo.getId(), "1-"));
                    return enInfoVO;
                }).collect(Collectors.toList());
            } else if (cmsContent) {
                //内容
                vos = vos.stream().map(e -> {
                    CmsContent content = this.cmsContentService.getById(e.getSourceId());
                    CmsContentVO cvo = blockDataConvert.toCmsContent(content);
                    cvo.setSourceId(e.getSourceId());
                    return cvo;
                }).collect(Collectors.toList());
            } else if (special) {
                vos = vos.stream().map(e -> {
                    SsSession session = this.sessionService.getById(e.getSourceId());
                    SessionVO sessionVO = blockDataConvert.toSession(session);
                    return sessionVO;
                }).collect(Collectors.toList());
            }
            vo.setBlockData(vos);
            return vo;
        } else {
            List<BlockVO> list = blockConvert.toVO(data);
            vo.setChildren(list);
            for (BlockVO datum : list) {
                this.child(datum);
            }
        }
        return vo;
    }
}
